#import "JVNetworkMonitor.h"
#import <arpa/inet.h>
#import <netdb.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
#import <SystemConfiguration/CaptiveNetwork.h>
#import "AFSecurityPolicy.h"
#import "AFURLSessionManager.h"
#import "ALUtils.h"
#import "JVMonitorCenter.h"
#import "ALDeviceInfoTools.h"
#import "PingTask.h"
#import "JVPharosNetworkModel.h"
#import "JVMonitorConfig.h"
#import <objc/runtime.h>
#import "LFXLoganPharosReportModel.h"
#import "JVPharosFirstTimeModel.h"

/**
 host:static.lufaxcdn.com
 ip:172.19.80.3
 carrier:中国联通/中国移动
 networkType:wifi(wifiSSID)/2g/3g/4g
 */

#define NETWORK_MONITOR_CATEGORY            @"category"
#define NETWORK_MONITOR_TITLE               @"title"
#define NETWORK_MONITOR_DATA                @"data"

#define NETWORK_MONITOR_CATEGORY_ANALYZE            @"network_analyze"

#define NETWORK_MONITOR_TITLE_SSL_START             @"network_ssl_connect_start"
#define NETWORK_MONITOR_TITLE_SSL_FAILED            @"network_ssl_connect_failed"
#define NETWORK_MONITOR_TITLE_DNS                   @"network_dns_query_failed"
#define NETWORK_MONITOR_TITLE_ERROR                 @"network_error_all"
#define NETWORK_MONITOR_TITLE_PING_TEST             @"network_error_ping_test"

#define NETWORK_MONITOR_TITLE_START                 @"network_before_call"
#define NETWORK_MONITOR_TITLE_FINISH                @"network_after_call"
#define NETWORK_MONITOR_TITLE_SUCCESS               @"network_success"

#define NETWORK_MONITOR_CATEGORY_NETWORK_REACHABLE      @"network_reachable"


/**
 CAT 监控
 */
#define NETWORK_MONITOR_CAT_UNKNOWN                 -100
#define NETWORK_MONITOR_CAT_NETWORK_UNAVAILABLE     -101
#define NETWORK_MONITOR_CAT_CANNOT_CONNECT_TO_HOST  -102
#define NETWORK_MONITOR_CAT_TIMEOUT                 -103
#define NETWORK_MONITOR_CAT_RESPONSE_ERROR          -104

static NSString * const MOITOR_QUEUE_NAME = @"com.jvnetetworkmonitor.serial";
static NSString * const IP_DIRECT_SERIAL_QUEUE = @"com.ip.direct.serial.queue";

@interface JVNetworkMonitor ()

@property (nonatomic, strong) dispatch_queue_t serialQueue;
@property (nonatomic, strong) dispatch_queue_t ipDirectSerialQueue;
@property (nonatomic, strong) AFNetworkReachabilityManager *reachableManager;
@property (nonatomic, assign) AFNetworkReachabilityStatus lastNetworkStatus;
@property (nonatomic, strong) NSArray<NSString *> *lumtResWhiteList;

@end

@implementation JVNetworkMonitor

+ (instancetype)instance {
    static dispatch_once_t onceToken;
    static JVNetworkMonitor *monitor;
    dispatch_once(&onceToken, ^{
        monitor = [[JVNetworkMonitor alloc] init];
    });
    return monitor;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _serialQueue = dispatch_queue_create([MOITOR_QUEUE_NAME UTF8String], DISPATCH_QUEUE_SERIAL);
        _ipDirectSerialQueue = dispatch_queue_create([IP_DIRECT_SERIAL_QUEUE UTF8String], DISPATCH_QUEUE_SERIAL);
        _lastNetworkStatus = AFNetworkReachabilityStatusUnknown;
        _isSSLHook = NO;
    }
    return self;
}

- (void)start {
    [self hookSSLURLSessionDidComplete];
//    if ([gFlexibleConfig getSSLHookSwitch]) {
//        [self hookSSLTrust];
//    }
    //ip直连
//    if ([gFlexibleConfig getIPDirectConfig].ip_connection_open) {
//        if (false) {
//        [self checkNetworkStatus];
//        [[JVIPScoreManager sharedInstance] setIPScoreStatusChangeBlock:^(JVIPScoreStatus status, NSString *domain, JVIPScoreList *ipItem) {
//            self.ipScoreStatus = status;
//            NSString *step = @"";
//            NSString *log = @"";
//            switch (status) {
//                case JVIPSpeedDefault:break;
//                case JVIPSpeedTestStart:
//                    // 测速开始
//                    log = [NSString stringWithFormat:@"speed_test_start"];
//                    step = @"speed_test_start";
//                    break;
//                case JVIPSpeedTestFinish:
//                    log = [NSString stringWithFormat:@"speedtest_finish:%@|%@",domain,ipItem.ip];
//                    step = @"speedtest_finish";
//                    break;
//                case JVIPScoreChanged:
//                    // 分值变化
//                    break;
//                case JVIPriorityChanged:
//                    // IP优先级变化 切换IP
//                    break;
//                case JVIPScoreError:
//                    log = [NSString stringWithFormat:@"ip_direct_error"];
//                    step = @"ip_direct_error";
//                    break;
//                default:
//                    break;
//            }
//            [JVIPScoreManager log:step data:log];
//        }];
//
//    }
    
//    if ([gFlexibleConfig getIPDirectSwitch]) {
    if(true){
        // 检测网络状况变化
        [self networkStatusMonitor];
    }
}

// 监听网络变化
- (void)networkStatusMonitor {
    if (self.reachableManager == nil) {
        self.reachableManager = [AFNetworkReachabilityManager manager];
        __weak __typeof(self) weakSelf = self;
        [self.reachableManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
            __strong __typeof(self) strongSelf = weakSelf;
            if (strongSelf.lastNetworkStatus != status) {
                NSString *log = [NSString stringWithFormat:@"network_type_change:%ld|%ld",(long)strongSelf.lastNetworkStatus,(long)status];
//                [JVIPScoreManager log:@"network_type_change" data:log];
                dispatch_async(_ipDirectSerialQueue, ^{
                    // 获取DNS结果，加入IP列表
                    NSString *DNSIP = [JVNetworkMonitor getIpAddressForDomain:@"ma.lu.com"];
//                    [gFlexibleConfig getIPDirectConfig].dnsIP = DNSIP;
//                    if ([gFlexibleConfig getIPDirectConfig].ip_connection_open) {
//                        // IP直连重新测速
//                        [[JVIPScoreManager sharedInstance] clearStatus];
//                        [[JVIPScoreManager sharedInstance] startIPSpeedTest];
//                    }
                });
            }
        }];
        [_reachableManager startMonitoring];
    }
}


//- (void)hookSSLTrust {
//    _isSSLHook = YES;
//    [AFSecurityPolicy aspect_hookSelector:@selector(evaluateServerTrust:forDomain:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> info, SecTrustRef serverTrust, NSString *domain) {
//        JVIPListManager *ipManager = [JVIPListManager sharedInstance];
//        if ([ipManager isValidIP:domain]) {// IP直连
//            NSString *__domain = [ipManager getDomainByip:domain];
//            if (__domain!=nil && __domain.length > 0) {
//                domain = __domain;
//            }
//        }
//
//
//        BOOL result;
//        NSInvocation *invocation = info.originalInvocation;
//        // Call original implementation.
//        [invocation setArgument:&domain atIndex:3];
//        if (![AFSecurityPolicy evaluateDomain:domain]) {
//            result = [AFSecurityPolicy evaluateServerTrustSSLPinningModeNone:serverTrust forDomain:domain];
//            [invocation setReturnValue:&result];
//        }else{
//            [invocation invoke];
//            [invocation getReturnValue:&result];
//        }
//        if (!result) {
//            // 2、ssl握手失败
//            dispatch_async(_serialQueue, ^{
//                NSString *ip = [JVNetworkMonitor getIpAddressForDomain:domain];
//
//                //                NSMutableString *certificates = [NSMutableString new];
//                //                if (serverTrust) {
//                //                    //获取证书摘要
//                //                    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
//                //                    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
//                //                    for (CFIndex i = 0; i < certificateCount; i++) {
//                //                        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
//                //                        [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
//                //                        NSString *cerStr = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificate);
//                //                        if (cerStr != nil && [cerStr isKindOfClass:[NSString class]]) {
//                //                            [certificates appendFormat:@"%@", [NSString stringWithFormat:@"[%@]",cerStr]];
//                //                        }
//                //                    }
//                //                }
//
//                NSDictionary *data = @{@"host":domain?:@"",
//                                       @"ip":ip?:@"",
//                                       @"carrier":[ALDeviceInfoTools carrierName]?:@"",
//                                       @"wifiSSID":wifiSSID()?:@""};
//
//                [[LFTracker instance] recordEvent:@{NETWORK_MONITOR_CATEGORY:NETWORK_MONITOR_CATEGORY_ANALYZE,
//                                                    NETWORK_MONITOR_TITLE:NETWORK_MONITOR_TITLE_SSL_FAILED,
//                                                    NETWORK_MONITOR_DATA:data?:@""}
//                                      categoryKey:NETWORK_MONITOR_CATEGORY
//                                        actionKey:NETWORK_MONITOR_TITLE
//                                         labelKey:nil
//                                         valueKey:nil
//                                           buName:BN_MainApp
//                                          logType:LogType_DEV];
//            });
//        };
//    }error:NULL];
//}

/**
 AFURLSessionManager
 - (void)URLSession:(NSURLSession *)session
 task:(NSURLSessionTask *)task
 didCompleteWithError:(NSError *)error
 */

- (void)hookSSLURLSessionDidComplete {
//    [AFURLSessionManager aspect_hookSelector:@selector(URLSession:task:didCompleteWithError:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> info, NSURLSession * session, NSURLSessionTask *task, NSError *error) {
//        id<JVHttpRequestProtocol> requestModel = objc_getAssociatedObject(task, ASSOCIATED_REQUEST_MODEL);
//        if (requestModel!=nil) {
//            // 计算端到端时间
//            if ([requestModel respondsToSelector:@selector(setResponseTime:)]) {
//                NSTimeInterval responseTime = [[NSDate new] timeIntervalSince1970] - [[requestModel startTime] timeIntervalSince1970];
//                [requestModel setResponseTime:responseTime];
//            }
//
//        }
//    } error:NULL];
}

+ (NSString*)getIpAddressForDomain:(NSString*)domain
{
    struct hostent *host = gethostbyname([domain UTF8String]);
    if(!host) {
        return nil;
    }
    struct in_addr **list = (struct in_addr **)host->h_addr_list;
    NSString *addressString = [NSString stringWithCString:inet_ntoa(*list[0]) encoding:NSUTF8StringEncoding];
    
    if (!addressString) {
        // 3、DNS解析失败
        NSLog(@"NDS Error!");
        NSDictionary *data = @{@"host":domain?:@"",
                               @"carrier":[ALDeviceInfoTools carrierName]?:@"",
                               @"wifiSSID":wifiSSID()?:@""};
//        [[LFTracker instance] recordEvent:@{NETWORK_MONITOR_CATEGORY:NETWORK_MONITOR_CATEGORY_ANALYZE,
//                                            NETWORK_MONITOR_TITLE:NETWORK_MONITOR_TITLE_DNS,
//                                            NETWORK_MONITOR_DATA:data?:@""}
//                              categoryKey:NETWORK_MONITOR_CATEGORY
//                                actionKey:NETWORK_MONITOR_TITLE
//                                 labelKey:nil
//                                 valueKey:nil
//                                   buName:BN_MainApp
//                                  logType:LogType_DEV];
        //        NSLog(@"\n=============DNS Error=============\n%@\n=============================================\n",data);
    }
    
    return addressString;
}

- (void)networkStart:(id)requestModel{
    NSNumber *networkAnalyzeSwitch = [[NSUserDefaults standardUserDefaults] objectForKey:@"networkAnalyzeSwitch"];
    if ([networkAnalyzeSwitch boolValue]) {
        dispatch_async(_serialQueue, ^{
//            NSString *url = [JVNetworkMonitor getUrlWithoutRandomNum:[requestModel requestURL]];
            NSString *url = [JVNetworkMonitor getUrlWithoutRandomNum:@"localhost"];
            NSDictionary *data = @{@"carrier":[ALDeviceInfoTools carrierName]?:@"",
                                   @"wifiSSID":wifiSSID()?:@"",
                                   @"url":url?:@""};
            // 7、网络请求发起
//            [[LFTracker instance] recordEvent:@{NETWORK_MONITOR_CATEGORY:NETWORK_MONITOR_CATEGORY_ANALYZE,
//                                                NETWORK_MONITOR_TITLE:NETWORK_MONITOR_TITLE_START,
//                                                NETWORK_MONITOR_DATA:data}
//                                  categoryKey:NETWORK_MONITOR_CATEGORY
//                                    actionKey:NETWORK_MONITOR_TITLE
//                                     labelKey:nil
//                                     valueKey:nil
//                                       buName:BN_MainApp
//                                      logType:LogType_DEV];
        });
    }
}

- (void)networkMonitor:(NSURLResponse * __unused )response responseObject:(id)responseObject error:(NSError *)error requestModel:(id)requestModel
{
    NSNumber *networkAnalyzeSwitch = [[NSUserDefaults standardUserDefaults] objectForKey:@"networkAnalyzeSwitch"];
    
    dispatch_async(_serialQueue, ^{
//        NSTimeInterval responseTime = [requestModel responseTime];
        NSTimeInterval responseTime = 1000;
        NSString *delay = [NSString stringWithFormat:@"%f",responseTime]?:@"";
        NSString *requestCode = @"";
//        NSDictionary *requestData = [requestModel requestData];
        //TODO: 修改
        NSDictionary *requestData = [NSDictionary new];
        if (requestData && [requestData isKindOfClass:[NSDictionary class]]) {
            requestCode = [requestData objectForKey:@"requestCode"];
        }
//        NSString *url = [JVNetworkMonitor getUrlWithoutRandomNum:[requestModel requestURL]];
        //TODO: 修改
        NSString *url = @"todourl";
        
        NSString *statusCode = @"";
        NSString *errorCode = @"";
        if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
            NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response;
            statusCode = [NSString stringWithFormat:@"%ld",(long)urlResponse.statusCode];
        }
        
        if (error) {
            // 4、网络请求失败
            NSString    *errorDes = [[error userInfo] valueForKey:@"NSLocalizedDescription"];
            errorCode = [NSString stringWithFormat:@"%ld",(long)error.code]?:@"";
            
            NSDictionary *data = @{@"errorCode":errorCode?:@"",
                                   @"errorDes":errorDes?:@"",
                                   @"url":url?:@"",
                                   @"httpCode":statusCode?:@"",
                                   @"delay":delay?:@"",
                                   @"carrier":[ALDeviceInfoTools carrierName]?:@"",
                                   @"requestCode":requestCode?:@"",
                                   @"wifiSSID":wifiSSID()?:@""};
            
//            [[LFTracker instance] recordEvent:@{NETWORK_MONITOR_CATEGORY:NETWORK_MONITOR_CATEGORY_ANALYZE,
//                                                NETWORK_MONITOR_TITLE:NETWORK_MONITOR_TITLE_ERROR,
//                                                NETWORK_MONITOR_DATA:data?:@""}
//                                  categoryKey:NETWORK_MONITOR_CATEGORY
//                                    actionKey:NETWORK_MONITOR_TITLE
//                                     labelKey:nil
//                                     valueKey:nil
//                                       buName:BN_MainApp
//                                      logType:LogType_DEV];
            
            NSLog(@"\n=============JVNetwork Error=============data:%@=============================================",data.description);
            
            
        }else {
            if ([networkAnalyzeSwitch boolValue])
            {
                // 5、网络请求成功
                NSDictionary *data = @{@"httpCode":statusCode?:@"",
                                       @"delay":delay?:@"",
                                       @"carrier":[ALDeviceInfoTools carrierName]?:@"",
                                       @"requestCode":requestCode?:@"",
                                       @"url":url?:@""};
               
            }
        }
        if ([networkAnalyzeSwitch boolValue])
        {
            // 6、网络请求全部结束
            
        }
    });
    
    // close ping test. by WiLL 2017-05-26
    
}

+ (NSString *)getUrlWithoutRandomNum:(NSString *)url {
    NSString *result = url;
    if ([ALStringHelper isEqual:url]) {
        NSUInteger randomNumLocation = [url rangeOfString:@"_"].location;
        if (randomNumLocation != NSNotFound) {
            result = [url substringToIndex:randomNumLocation];
        }
    }
    return result?:@"";
}

#pragma mark - ------------------------- Tools -------------------------

NSString *wifiSSID() {
    @try {
        NSString *wifiName = nil;
        CFArrayRef myArray = CNCopySupportedInterfaces();
        if (myArray != nil) {
            CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
            if (myDict != nil) {
                NSDictionary *dict = (NSDictionary*)CFBridgingRelease(myDict);
                wifiName = [dict valueForKey:@"SSID"];
            }
        }
        return wifiName;
    }@catch (NSException *exception) {
        return @"";
    }
}

NSString *networkType() {
    NSString *result = @"";
    NSString *networkType =  [ALHttpUtils newtworkType];
    if (networkType != nil && [networkType isEqualToString:@"Wifi"]) {
        result = @"wifi";
    }else {
        result = networkType;
    }
    
    return result;
}

- (void)checkNetworkStatus {
    NSString *newtworkType = [ALHttpUtils newtworkType];
    BOOL reachable = NO;
    if (newtworkType != nil && newtworkType.length > 0) {
        reachable = YES;
    }
    
}


#pragma mark  -------------------- 接口过滤 -------------------------

- (void)httpRequestFilter:(id)requestModel pharosModel:(JVPharosRootModel *)pharosModel domain:(NSString *)domain {
//    if (pharosModel && [pharosModel isKindOfClass:[JVPharosNetworkModel class]]) {
//        JVPharosNetworkModel *networkModel = (JVPharosNetworkModel *)pharosModel;
//        eJVNetworkConnectionType connType = [requestModel netwrokConnectionType];
//        if (connType == eJVNetworkConnectionTypeHTTPIP || connType == eJVNetworkConnectionTypeHTTPSpecificIP) {
//            NSString *ip = [[requestModel remoteIP] copy];
//            NSDictionary *transactionMetrics = [requestModel transactionMetrics];
//            NSTimeInterval endToEndTime = [requestModel responseTime];
//            NSTimeInterval responseTime = 0;
//            if (transactionMetrics && [transactionMetrics isKindOfClass:[NSDictionary class]]) {
//                responseTime = [[transactionMetrics objectForKey:@"responseTime"] doubleValue];
//            }
//            NSString *httpCode = networkModel.hc;
//            NSTimeInterval networkTime = endToEndTime - responseTime;
//            JVIPScoreItem *ipScoreItem = [JVIPScoreItem new];
//            ipScoreItem.ip = ip;
//            ipScoreItem.domain = domain;
//            ipScoreItem.rtt = (int)(networkTime*1000);
//            NSSet *successCode = [[JVMonitorConfig sharedInstance].monitorConfigDelegate httpSuccessCodes];
//
//            if (httpCode!=nil && [successCode containsObject:httpCode]) {
//                ipScoreItem.r = 0;
//            }else{
//                ipScoreItem.r = 1;
//            }
//            [[JVIPScoreManager sharedInstance] record:ipScoreItem];
//            NSLog(@"\n======================================\n    remote address:%@ \n    end to end time:%f \n    http code:%@ \n======================================\n",ip,networkTime,httpCode);
//        }
//    }
}

#pragma mark  -------------------- CAT 监控 -------------------------

- (void)catMonitor:(id)requestModel sessionTask:(NSURLSessionTask *)sessionTask responseData:(NSData *)responseData urlResponse:(NSURLResponse *)urlResponse error:(NSError *)error{
    int64_t countOfBytesSent =  0; // request body大小
    if (sessionTask != nil && [sessionTask isKindOfClass:[NSURLSessionTask class]]) {
        countOfBytesSent =  sessionTask.countOfBytesSent;
    }
    NSNumber *catSwitch = [[NSUserDefaults standardUserDefaults] objectForKey:@"catSwitch"];
    LFXCatSwitchLevel catSwitchLevel = [[JVMonitorConfig sharedInstance].monitorConfigDelegate catUploadSwitchLevel];
//    if (!requestModel || ![requestModel conformsToProtocol:@protocol(JVHttpRequestProtocol)]) {
//        return;
//    }
    // 线程切换，copy retrycount
//    NSInteger copy_currentRetryCount = requestModel.currentRetryCount;
    NSInteger copy_currentRetryCount = 3;
    
    dispatch_async(_serialQueue, ^{
        int64_t responseBytes=0;
        NSString* businessCode;
        NSString* subcode;
        NSString* message;
        if(responseData){
            responseBytes = [responseData length];
            // 重复反序列化接口返回结果，后续需要优化
            NSString *responseStr = nil;
            if ([responseData isKindOfClass:[NSData class]]) {
                responseStr = STRING_FROM_DATA(responseData);
            }
            if (!responseStr) {
                // 返回数据非UTF-8 可能不是陆金所的接口
                return;
            }else{
                NSDictionary* __dic = JSON_DIC_FROM_STRING(responseStr);
                
                if (__dic) {
                    NSSet *code_keys = [[JVMonitorConfig sharedInstance].monitorConfigDelegate serverResponseCode];
                    NSString * code = [__dic objectForKey:@"code"];
                    if (code == nil) {
                        for (NSString *code_key in code_keys) {
                            code = [__dic objectForKey:code_key];
                            if ([code isKindOfClass:[NSString class]] && code.length > 0) {
                                break;
                            }
                        }
                    }
                    if ([code isKindOfClass:[NSString class]] && code.length>0 ) {
                        businessCode = code;
                    }
                    
                    NSSet *subcode_keys = [[JVMonitorConfig sharedInstance].monitorConfigDelegate serverResponseSubCode];
                    subcode = [[__dic objectForKey:@"subcode"] description];
                    if (subcode == nil) {
                        for (NSString *subcode_key in subcode_keys) {
                            subcode = [[__dic objectForKey:subcode_key] description];
                            if (subcode != nil &&
                                [subcode isKindOfClass:[NSString class]] &&
                                subcode.length > 0) {
                                break;
                            }
                        }
                    }
                    
                    // message
                    NSSet *message_keys = [[JVMonitorConfig sharedInstance].monitorConfigDelegate serverResponseMessage];
                    message = [__dic objectForKey:@"message"];
                    if (message == nil) {
                        for (NSString *message_key in message_keys) {
                            message = [[__dic objectForKey:message_key] description];
                            if (message != nil &&
                                [message isKindOfClass:[NSString class]] &&
                                message_key.length > 0) {
                                break;
                            }
                        }
                    }
                }
                else {
                    BOOL isHtml = [JVNetworkMonitor isHtmlContent:urlResponse];
                    NSString *trimmedResponseStr = @"";
                    if (isHtml || ([trimmedResponseStr hasPrefix:@"<"] && [trimmedResponseStr hasSuffix:@">"])) {
                        //HTML page, just return 'null' as android
                        businessCode = @"null";
                    }
                    else {
                        businessCode = [NSString stringWithFormat:@"%d",NETWORK_MONITOR_CAT_RESPONSE_ERROR]; //回传到数据格式错误
                    }
                }
            }
        }
        
        int64_t requestBytes = countOfBytesSent;
        
        JVPharosNetworkModel *itemModel = [JVPharosNetworkModel generatePharosItemModel:requestModel];
        
        NSURL *urlObject = [NSURL URLWithString:@"http://localhost"];
        if (urlObject == nil) {
            return;
        }
        itemModel.url_obj = urlObject;
        NSString* commandId = [JVNetworkMonitor catUrlFormat:urlObject];
        
        NSString *statusCode = nil;
        if (urlResponse && [urlResponse isKindOfClass:[NSHTTPURLResponse class]]) {
            NSHTTPURLResponse *httpURLResponse = (NSHTTPURLResponse *)urlResponse;
            statusCode = [NSString stringWithFormat:@"%ld",(long)httpURLResponse.statusCode];
            //Content-Encoding
            if ([ALStringHelper isEmpty:[httpURLResponse.allHeaderFields objectForKey:@"Content-Encoding"]] && [ALStringHelper isEmpty:[httpURLResponse.allHeaderFields objectForKey:@"content-encoding"]]) {
                itemModel.ng = 0;
            }else {
                itemModel.ng = 1;
            }
            // Content-Type
            if (![ALStringHelper isEmpty:[httpURLResponse.allHeaderFields objectForKey:@"Content-Type"]]) {
                itemModel.ct = [httpURLResponse.allHeaderFields objectForKey:@"Content-Type"];
            }
        }
        int httpCode = [JVNetworkMonitor httpCode:statusCode error:error];
        
        itemModel.url = commandId;
        itemModel.hc = [NSString stringWithFormat:@"%d",httpCode];
        itemModel.bc = [NSString stringWithFormat:@"%@",businessCode];

        if (![ALStringHelper isEmpty:subcode] && ![subcode containsString:@"null"]) {
            itemModel.bc = [NSString stringWithFormat:@"%@|%@",businessCode,subcode];
        }
        
        itemModel.qs = requestBytes;
        itemModel.ps = responseBytes;
        itemModel.page = @"";
        if (message != nil && [message isKindOfClass:[NSString class]] && message.length > 0) {
            itemModel.d = message;
        }
//        if ([requestModel netwrokConnectionType] == eJVNetworkConnectionTypeHTTPIP) {
//            itemModel.nc = 3;
//            itemModel.ne = [requestModel remoteIP];
//        }else {
            itemModel.nc = 0;
            itemModel.ne =@"";
//        }
        
        // 重试
//        if (requestModel.ifRetry) {
//            NSInteger currentCount = copy_currentRetryCount;
//            NSInteger maxRetry = requestModel.maxRetryCount;
//            if (maxRetry - currentCount > 0 ) {
//                itemModel.retry = @"middle";
//                itemModel.retryCount = currentCount+1;
//            }else if (maxRetry == currentCount) {
//                itemModel.retry = @"last";
//                itemModel.retryCount = maxRetry+1;
//            }
//        }
        
        //网络监控
        [self httpRequestFilter:requestModel pharosModel:itemModel domain:[urlObject host]];
        
        // 上报cat
        //由于ios10以下，itemmModel的tcp、dns、ssl、rq、rp都是硬编码为0，过滤不上报
        BOOL isHighIOS10 = ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0);
        //fetch type为local cache时不发生请求，不上报
//        BOOL isLocalCache = (([[[requestModel transactionMetrics]objectForKey:@"fetchType"]integerValue]==NSURLSessionTaskMetricsResourceFetchTypeLocalCache) &&
//                               itemModel.rp<=0 &&
//                               itemModel.rq<=0);
        BOOL isLocalCache = false;
        if (isHighIOS10 && !isLocalCache && ([catSwitch boolValue] || catSwitchLevel != LFXCatSwitchLevel0)) {
            [[JVMonitorCenter defaultCenter] track:[self isAbnormalTimeWithModel:itemModel requestModel:requestModel]];
            
            //record logan event
            [self recordLoganWithCommandId:commandId responseData:responseData];
        }
        
        //上报首屏加载时间
        NSDictionary *info = @{};
        if (info && [[info allKeys]count]>0 && httpCode == 200) {//接口成功
            JVPharosFirstTimeModel *timeItemModel = [JVPharosFirstTimeModel generatePharosItemModel:requestModel];
            [[JVMonitorCenter defaultCenter] track:timeItemModel];
        }
    });
}

- (void)recordLoganWithCommandId:(NSString *)commandId responseData:(NSData *)responseData {
//    if ([JVNetworkConfig sharedInstance].loganImplDelegate && [[JVNetworkConfig sharedInstance].loganImplDelegate respondsToSelector:@selector(isServiceEnabled)]) {//logan
//        if (![[JVNetworkConfig sharedInstance].loganImplDelegate isServiceEnabled]) {
//            return;
//        }
//    }

    BOOL isWhiteList = NO;
    NSArray *tLumtResWhiteList = self.lumtResWhiteList ?: @[@"appInitial/property"];
    for (NSString *aWhiteListUrl in tLumtResWhiteList) {
        if([commandId containsString:aWhiteListUrl]) {
            isWhiteList = YES;
            break;
        }
    }
    
    NSString *responseStr = @"";
    if ([responseData isKindOfClass:[NSData class]]) {
        responseStr = STRING_FROM_DATA(responseData);
    }
    
    static const NSUInteger kMaxLen = 300;
    if(!isWhiteList && responseStr.length > kMaxLen) {
        responseStr = [responseStr substringToIndex:kMaxLen];
    }
    
//    LFXLoganPharosReportModel *loganModel = [LFXLoganPharosReportModel modelWithCommandId:[NSString sf_getUnNilString:commandId]
//                                                                                 response:[NSString sf_getUnNilString:responseStr]];
//    [[JVMonitorCenter defaultCenter] pvWithLoganPharosModel:loganModel];//LFXLoganPharosReportModel, LFXLoganPharosReporter
}

- (NSArray<NSString *> *)lumtResWhiteList {
//    if(!_lumtResWhiteList) {
//        if ([JVNetworkConfig sharedInstance].loganImplDelegate && [[JVNetworkConfig sharedInstance].loganImplDelegate respondsToSelector:@selector(lumtResWhiteList)]) {//logan
//            NSArray<NSString *> *lumtResWhiteList = [[JVNetworkConfig sharedInstance].loganImplDelegate lumtResWhiteList];
//            if(![NSArray lfx_isEmptyOrNil:lumtResWhiteList]) {
//                _lumtResWhiteList = lumtResWhiteList;
//            }
//        }
//        else {
//            _lumtResWhiteList = @[];
//        }
//    }
    
    return _lumtResWhiteList;
}

- (JVPharosNetworkModel *)isAbnormalTimeWithModel:(JVPharosNetworkModel *)itemModel requestModel:(id)requestModel
{
//    if (itemModel.pt>THRESHOLD_TIME||itemModel.ttfb>THRESHOLD_TIME ||
//        itemModel.dns>THRESHOLD_TIME||itemModel.ssl>THRESHOLD_TIME ||
//        itemModel.tcp>THRESHOLD_TIME||itemModel.rq>THRESHOLD_TIME ||
//        itemModel.rp>THRESHOLD_TIME) {
//        itemModel.type = @"abnormalTime";
//        NSMutableDictionary *dic = [NSMutableDictionary new];
//        [dic addEntriesFromDictionary:itemModel.p];
//        if (requestModel.startTime) {
//            [dic setObject:requestModel.startTime forKey:@"startTime"];
//        }
//        itemModel.p = dic;
//        itemModel.domain = @"requestAbnormalTime";
//    }
    return itemModel;
}

+ (BOOL)isHtmlContent:(NSURLResponse *)urlResponse
{
    BOOL isHtml = NO;
    
    NSHTTPURLResponse *httpURLResponse = (NSHTTPURLResponse *)urlResponse;
    if (httpURLResponse && [httpURLResponse isKindOfClass:[NSHTTPURLResponse class]]) {
        NSString *ct = [httpURLResponse.allHeaderFields objectForKey:@"Content-Type"];
        if ([ct containsString:@"html"]) {
            isHtml = YES;
        }
    }
    
    return isHtml;
}

+(NSInteger)catNetWorkCode{
    /*networkType*/
    NSString* netWorkType = [ALHttpUtils newtworkType];
    NSInteger netType = 0;
    if([netWorkType isEqualToString:@"wifi"])netType = 1;
    else if([netWorkType isEqualToString:@"2G"])netType = 2;
    else if([netWorkType isEqualToString:@"3G"])netType = 3;
    else if([netWorkType isEqualToString:@"4G"])netType = 4;
    return netType;
}


+(NSString*)catUrlFormat:(NSURL*)url{
    NSString* path = [url path];
    NSString* query = [url query];
    if(query.length>0){
        NSArray* __arr = [query componentsSeparatedByString:@"&"];
        if ([__arr count] > 0) {
            // https://ma.lu.com/mapp/service/public?M6003&_=5962
            // /mapp/service/public/M6003
            // 简单匹配只把m-code的拼在path后面
            NSString *mcode = __arr[0];
            if ([mcode isKindOfClass:[NSString class]] && mcode.length > 0 && [mcode rangeOfString:@"="].location == NSNotFound) {
                path = [[path stringByAppendingString:@"/"] stringByAppendingString:mcode];
            }
        }
    }
    return path;
}

/**
 <code id="-100" name="-100 未知错误" status="1"></code>
 <code id="-101" name="-101 不能连接到网络" status="0"></code>
 <code id="-102" name="-102 无法建立连接（DNS或者建立连接错误）" status="1"></code>
 <code id="-103" name="-103 超时（一般发生在建立连接或发送阶段）" status="1"></code>
 <code id="-104" name="-104 回传到数据格式错误" status="1"></code>
 <code id="200" name="200 OK" status="0"></code>
 <code id="401" name="401 Unauthorized" status="1"></code>
 <code id="403" name="403 加签验签失败" status="1"></code>
 <code id="555" name="555 运营维护" status="1"></code>
 <code id="302" name="302 临时跳转" status="1"></code>
 <code id="400" name="400 Bad Request" status="1"></code>
 <code id="409" name="409 资源冲突" status="1"></code>
 <code id="418" name="418 I'm a Teapot" status="1"></code>
 <code id="450" name="450 服务器消息提示" status="1"></code>
 <code id="451" name="451 服务器消息提示" status="1"></code>
 <code id="500" name="500 Internal Server Error" status="1"></code>
 <code id="501" name="501 Not Implemented" status="1"></code>
 <code id="502" name="502 Bad Gateway" status="1"></code>
 <code id="503" name="503 Service Unavailable" status="1"></code>
 <code id="504" name="504 Gateway Timeout" status="1"></code>
 <code id="505" name="505 HTTP Version Not Supported" status="1"></code>
 <code id="0" name="0000 业务－成功" status="0"></code>
 <code id="1111" name="1111 业务－异常1" status="1"></code>
 <code id="2222" name="2222 业务－异常2" status="1"></code>
 <code id="9999" name="9999 业务－其他异常" status="1"></code>
 Status = 0 代表了成功
 */
+(int)httpCode:(NSString*)statusCode error:(NSError*)error{
    if (error) {
        if (statusCode!=nil && statusCode.length>0) {// 如果有标准的status code就上传
            return statusCode.intValue;
        }else{ // 没有status code而且出错了，应该是网络错误，上传网络错误code
            NSInteger errorCode = [error code];
            return (int)errorCode;
        }
    }else{
        return statusCode.intValue;
    }
}

/******************************************************************************/
/**** Ping Test                                                            ****/
/******************************************************************************/

#pragma mark  -------------------- Ping Test -------------------------

- (void)executePingTasks:(NSMutableDictionary *)pingInfo
{
    NSLog(@"Executed Ping Task Start >>>>");
    if (pingInfo == nil) {
        pingInfo = [NSMutableDictionary new];
    }
    
    // ping ma.lu.com
    PingTask *maTask = [PingTask pingTaskWithHost:@"ma.lu.com"
                                       retryCount:5
                                          timeout:6
                                             data:nil
                                       pingMethod:PingMethodICMP];
    PingTaskResult *maResult = [maTask doTask];
    [pingInfo setValue:[maResult output] forKey:@"ma.lu.com"];
    NSLog(@"ma ping task finish : %@", [maResult output]);
    
    
    // ping mapp.lu.com
    PingTask *mappTask = [PingTask pingTaskWithHost:@"mapp.lu.com"
                                         retryCount:5
                                            timeout:6
                                               data:nil
                                         pingMethod:PingMethodICMP];
    PingTaskResult *mappResult = [mappTask doTask ];
    [pingInfo setValue:[mappResult output] forKey:@"mapp.lu.com"];
    NSLog(@"mapp ping task finish : %@", [mappResult output]);
    
    
    // ping ms.lu.com
    PingTask *msTask = [PingTask pingTaskWithHost:@"ms.lu.com"
                                       retryCount:5
                                          timeout:6
                                             data:nil
                                       pingMethod:PingMethodICMP];
    PingTaskResult *msResult = [msTask doTask];
    [pingInfo setValue:[msResult output] forKey:@"ms.lu.com"];
    NSLog(@"ms ping task finish : %@", [msResult output]);
    
    
    // ping unicom 210.22.70.3
    PingTask *unicomTask = [PingTask pingTaskWithHost:@"210.22.70.3"
                                           retryCount:5
                                              timeout:6
                                                 data:nil
                                           pingMethod:PingMethodICMP];
    PingTaskResult *unicomResult = [unicomTask doTask];
    [pingInfo setValue:[unicomResult output] forKey:@"unicom"];
    NSLog(@"unicom ping task finish : %@", [unicomResult output]);
    
    
    // ping telecom 210.22.70.3
    PingTask *telecomTask = [PingTask pingTaskWithHost:@"202.96.209.5"
                                            retryCount:5
                                               timeout:6
                                                  data:nil
                                            pingMethod:PingMethodICMP];
    PingTaskResult *telecomResult = [telecomTask doTask];
    [pingInfo setValue:[telecomResult output] forKey:@"telecom"];
    NSLog(@"telecom ping task finish : %@", [telecomResult output]);
    
    // ping www.baidu.com
    PingTask *baiduTask = [PingTask pingTaskWithHost:@"www.baidu.com"
                                          retryCount:5
                                             timeout:6
                                                data:nil
                                          pingMethod:PingMethodICMP];
    PingTaskResult *baiduResult = [baiduTask doTask];
    [pingInfo setValue:[baiduResult output] forKey:@"baidu"];
    NSLog(@"baidu ping task finish : %@", [baiduResult output]);
    
    // ping www.163.com
    PingTask *neteaseTask = [PingTask pingTaskWithHost:@"www.163.com"
                                            retryCount:5
                                               timeout:6
                                                  data:nil
                                            pingMethod:PingMethodICMP];
    PingTaskResult *neteaseResult = [neteaseTask doTask];
    [pingInfo setValue:[neteaseResult output] forKey:@"netease"];
    NSLog(@"netease ping task finish : %@", [neteaseResult output]);
    
    NSLog(@"Executed Ping Task End <<<<");
}

@end
